/**
 * Copyright (C), 2015-2019, XXX有限公司
 * FileName: GoldenSecurityConfig
 * Author:   zhouheng
 * Date:     2019/6/7 17:01
 * Description: security 配置类
 * History:
 * <author>          <time>          <version>          <desc>
 * 作者姓名           修改时间           版本号              描述
 */
package com.zhouh.golden.security.config;

import com.zhouh.golden.common.constants.GoldenConstant;
import com.zhouh.golden.security.code.ValidateCodeGenerator;
import com.zhouh.golden.security.code.img.ImageCodeFilter;
import com.zhouh.golden.security.code.img.ImageCodeGenerator;
import com.zhouh.golden.security.code.sms.SmsCodeFilter;
import com.zhouh.golden.security.handler.GoldenAuthenticationAccessDeniedHandler;
import com.zhouh.golden.security.handler.GoldenAuthenticationFailureHandler;
import com.zhouh.golden.security.handler.GoldenAuthenticationSucessHandler;
import com.zhouh.golden.security.handler.GoldenLogoutHandler;
import com.zhouh.golden.security.properties.GoldenSecurityProperties;
import com.zhouh.golden.security.service.GoldenUserDetailService;
import com.zhouh.golden.security.session.GoldenExpiredSessionStrategy;
import com.zhouh.golden.security.session.GoldenInvalidSessionStrategy;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.session.SessionRegistry;
import org.springframework.security.core.session.SessionRegistryImpl;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutHandler;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
import org.springframework.security.web.session.InvalidSessionStrategy;
import org.springframework.social.security.SpringSocialConfigurer;

import javax.sql.DataSource;

/**
 * 〈一句话功能简述〉<br> 
 * 〈security 配置类〉
 *
 * @author zhouheng
 * @create 2019/6/7
 * @since 1.0.0
 */
@Slf4j
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class GoldenSecurityConfig extends WebSecurityConfigurerAdapter {
    @Bean
    public A a(){
        return new A();
    }

    @Autowired
    private GoldenSecurityProperties goldenSecurityProperties;
    @Autowired
    private DataSource dataSource;
    @Autowired
    private GoldenAuthenticationSucessHandler goldenAuthenticationSucessHandler;
    @Autowired
    private GoldenAuthenticationFailureHandler goldenAuthenticationFailureHandler;
    @Autowired
    private GoldenUserDetailService goldenUserDetailService;
    @Autowired
    private SpringSocialConfigurer febsSocialSecurityConfig;

    @Autowired
    private GoldenSmsCodeAuthenticationSecurityConfig goldenSmsCodeAuthenticationSecurityConfig;

    // 使用 javaconfig 的方式配置是为了注入 sessionRegistry
    @Bean
    public GoldenAuthenticationSucessHandler goldenAuthenticationSucessHandler() {
        GoldenAuthenticationSucessHandler authenticationSucessHandler = new GoldenAuthenticationSucessHandler();
        authenticationSucessHandler.setSessionRegistry(sessionRegistry());
        return authenticationSucessHandler;
    }

    // spring security自带的密码加密工具类
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
//

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        super.configure(auth);
    }

    // 处理 rememberMe 自动登录认证
    @Bean
    public PersistentTokenRepository persistentTokenRepository() {
        JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
        jdbcTokenRepository.setDataSource(dataSource);
        jdbcTokenRepository.setCreateTableOnStartup(false);
        return jdbcTokenRepository;
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 获取登陆成功后返回的url
        String[] anonResourcesUrl = StringUtils.splitByWholeSeparatorPreserveAllTokens(goldenSecurityProperties.getAnonResourcesUrl(),",");

        ImageCodeFilter imageCodeFilter = new ImageCodeFilter();
        imageCodeFilter.setAuthenticationFailureHandler(goldenAuthenticationFailureHandler);
        imageCodeFilter.setSecurityProperties(goldenSecurityProperties);
        imageCodeFilter.afterPropertiesSet();

        SmsCodeFilter smsCodeFilter = new SmsCodeFilter();
        smsCodeFilter.setAuthenticationFailureHandler(goldenAuthenticationFailureHandler);
        smsCodeFilter.setSecurityProperties(goldenSecurityProperties);
        smsCodeFilter.setSessionRegistry(sessionRegistry());
        smsCodeFilter.afterPropertiesSet();

        http
                .addFilterBefore(smsCodeFilter, UsernamePasswordAuthenticationFilter.class)
                .addFilterBefore(imageCodeFilter, UsernamePasswordAuthenticationFilter.class)
                .formLogin()
                .loginPage(goldenSecurityProperties.getLoginUrl()) // 未认证跳转 URL
                .loginProcessingUrl(goldenSecurityProperties.getCode().getImage().getLoginProcessingUrl()) // 处理登录认证 URL
                .successHandler(goldenAuthenticationSucessHandler) // 处理登录成功
                .failureHandler(goldenAuthenticationFailureHandler) // 处理登录失败
                .and()
                    .rememberMe() // 添加记住我功能
                    .tokenRepository(persistentTokenRepository()) // 配置 token 持久化仓库
                    .tokenValiditySeconds(goldenSecurityProperties.getRememberMeTimeout()) // rememberMe 过期时间，单为秒
                    .userDetailsService(goldenUserDetailService) // 处理自动登录逻辑
                .and()
                    .sessionManagement() // 配置 session管理器
                    .invalidSessionStrategy(invalidSessionStrategy()) // 处理 session失效
                    .maximumSessions(goldenSecurityProperties.getSession().getMaximumSessions()) // 最大并发登录数量
                    .expiredSessionStrategy(new GoldenExpiredSessionStrategy()) // 处理并发登录被踢出
                    .sessionRegistry(sessionRegistry()) // 配置 session注册中心
                .and()
                .and()
                    .logout() // 配置登出
                    .addLogoutHandler(logoutHandler()) // 配置登出处理器
                    .logoutUrl(goldenSecurityProperties.getLogoutUrl()) // 处理登出 url
                    .logoutSuccessUrl("/") // 登出后跳转到 /
                    .deleteCookies("JSESSIONID") // 删除 JSESSIONID
                .and()
                    .authorizeRequests() // 授权配置
                    .antMatchers(anonResourcesUrl).permitAll() // 免认证静态资源路径
                    .antMatchers(
                            "/user/test",
                            goldenSecurityProperties.getLoginUrl(), // 登录路径
                            GoldenConstant.GOLDEN_REGIST_URL, // 用户注册 url
                            goldenSecurityProperties.getCode().getImage().getCreateUrl(), // 创建图片验证码路径
                            goldenSecurityProperties.getCode().getSms().getCreateUrl() // 创建图片验证码路径
                    )
                    .permitAll() // 配置免认证路径
                    .anyRequest()  // 所有请求
                    .authenticated() // 都需要认证
                .and()
                    .csrf().disable()
                    .apply(goldenSmsCodeAuthenticationSecurityConfig)
                .and()
                    .apply(febsSocialSecurityConfig);
    }

    @Bean
    public InvalidSessionStrategy invalidSessionStrategy(){
        GoldenInvalidSessionStrategy febsInvalidSessionStrategy = new GoldenInvalidSessionStrategy();
        febsInvalidSessionStrategy.setGoldenSecurityProperties(goldenSecurityProperties);
        return febsInvalidSessionStrategy;
    }

    // 配置登出处理器
    @Bean
    public LogoutHandler logoutHandler(){
        GoldenLogoutHandler goldenLogoutHandler = new GoldenLogoutHandler();
        goldenLogoutHandler.setSessionRegistry(sessionRegistry());
        return goldenLogoutHandler;
    }

    @Bean
    public AccessDeniedHandler accessDeniedHandler() {
        return new GoldenAuthenticationAccessDeniedHandler();
    }

    @Bean
    @ConditionalOnMissingBean(name = "imageCodeGenerator")
    public ValidateCodeGenerator imageCodeGenerator() {
        ImageCodeGenerator imageCodeGenerator = new ImageCodeGenerator();
        imageCodeGenerator.setSecurityProperties(goldenSecurityProperties);
        return imageCodeGenerator;
    }

    @Bean
    public SessionRegistry sessionRegistry() {
        return new SessionRegistryImpl();
    }
}